from base import *
import numpy as np
from numpy.random import binomial
from random import gauss
from pylab import zeros
from extra import*
import time
cimport numpy

def shift(l, n):
    """This shifts list l by n"""
    return l[n:] + l[:n]

class cflight:
    """Very basic class defining flights, overwrite flight imported from base"""
#    cdef int id, pushbacktime, departuretime, Npushback, Ndeparture, aNpushback, aNdeparture
    def __init__(self,_id,pushbacktime,departuretime=None,Npushback=None,aNpushback=None):
        self.id = _id
        self.pushbacktime = pushbacktime
        self.departuretime = departuretime
        self.Npushback = Npushback
        self.Ndeparture = None
        self.aNpushback = aNpushback
        self.aNdeparture = None
        
cdef class Solver:
    cdef public int id_,dt,run,n,binomial_n
    cdef public float tau_unimped_mean, tau_unimped_std,binomial_p,alpha
    cdef public numpy.ndarray T,R,Q,P,N,aT,aN,D,aD,at,t,sstd_
    cdef public list tt, orig, mean_, std_
    cdef public list day_list
    cdef public dict flights, orig_
    
    """
    Solver is a base class which simulate departure process using model given in

    I. Simaiakis and H. Balakrishnan, "Queuing Models of Airport Departure Processes
    for Emissions Reduction," AIAA, 2009, pp. 1-26.



    Paramters # # # #
    =========  # # # #

    Simulation
    
    run    <type: int> Number of runs required (default 100)
    dt     <type: int> Time step(min) for which update is performed (default 5)
    day    <type: str> Format yyyy-mm-dd (default 2009-08-01)

    Delay Model

    tau_unimped_mean <type: float> Mean of unimped taxi time (default 10.0)
    tau_unimped_std  <type: float> std of unimped taxi time (default 4.0)
    
    
    Runway Service
    binomial_n <type: int>   (default 4)
    binomial_p <type: float> (default .45)
    

    
    Variables
    ==========
    n: Length of Variables
    = (24*60/self.dt)*len(self.day_list)*self.run

    T: T(t) Takeoff during time period t
    R: R(t) Number of taxiing aircraft at time t
    Q: Q(t) Number of aircraft in queue at time t
    P: P(t) Number of pushback during time period t
    N: R(t) + Q(t)
    D: D(t) Avg delay of aircraft taking off during time period t

    aT: Actual T
    aN: Actual N
    aD: Actual delay

    alpha: Coefficient of R in the time delay

    Post Processing
    ===============
    You need to divide all the variable arrays into a 2D array of shape run x n/run
    Take Mean and std of rows.
    Naming convention, insert "_" before any variable array

    Impletmentations
    ================

    This is not mentioned anywhere and done by me. I hope its correct!

    Calculating max TakeOff possible during time period
    ---------------------------------------------------
    
    total_time = 0
    takeoffcount = 0
    while total_time <= dt:
        takeoffcount += 1
        total_time += binomial(binomial_n.binomial_p)

    Although the above approach will work properly for dt > binomial_n,
    but will give some random results if dt is made small. 
    
    Shifting flights from R to Q
    -----------------------------
    tau is calculated for all the flights that are pushback during time period t.
    Then if depending upon the interval in which tau lies (n*dt,(n+1)*dt), all the
    flights in this interval and interval greater then this are added to R corresponding
    to n.
    
    Calculating delay
    -----------------
    To calculate delay we arrange flights according to their pushback time, and depart
    them one by one. And note the departing time.


    Functions
    ===========
    _initbase()
    Initialises everything, create new flights, calculate actual parameters.
    """


    def __init__(self,run=100,dt=5,day='2009-08-01',tau_unimped_mean=10.93,\
                 tau_unimped_std=4.09,binomial_n=4,binomial_p=.45):
        self.id_ = 0
        self.dt = dt
        self.run = run
        self.day_list = [day]
        self.tau_unimped_mean = tau_unimped_mean
        self.tau_unimped_std = tau_unimped_std
        self.binomial_n = binomial_n
        self.binomial_p = binomial_p
        
        self.n = (24*60/self.dt)*len(self.day_list)*self.run

        self.T = zeros(self.n)
        self.R = zeros(self.n)
        self.Q = zeros(self.n)
        self.P = zeros(self.n)
        self.N = zeros(self.n)
        self.aT = zeros(self.n)
        self.aN = zeros(self.n)
        self.D = zeros(self.n)
        self.aD = zeros(self.n)
        self.alpha = 0.12


    def _initbase(self):

        _counter = 0
        os.chdir('/home/anand/flightdata/npz')
        self.flights = {}
      
        for day in self.day_list:
            filename="BOS_"+day+".npz"
            file_ = np.load(filename)
            orig = file_['orig']
            
            self.orig_ = {}
            id_ = 0
            for j in orig:
                
                #j.taxi_D = j.a_runway_departure - j.a_departure_time_gate
                sr = str(j.a_runway_departure)
                sg = str(j.a_departure_time_gate)
                
                if len(sr) == 4:
                    sr_hr = int(sr[:2])
                    sr_min = int(sr[2:])
                if len(sr) == 3:
                    sr_hr = int(sr[:1])
                    sr_min = int(sr[1:])
                if len(sr) <= 2:
                    sr_hr = 0
                    sr_min = int(sr)
                        
                if len(sg) == 4:
                    sg_hr = int(sg[:2])
                    sg_min = int(sg[2:])
                if len(sg) == 3:
                    sg_hr = int(sg[:1])
                    sg_min = int(sg[1:])
                if len(sg) <= 2:
                    sg_hr = 0
                    sg_min = int(sg)

                j.taxi_delay = (sr_hr-sg_hr)*60 + (sr_min-sg_min)

                if j.missing_info == False and j.taxi_delay > 0:#: and j.taxi_delay <= 44:
                    j.delay = j.taxi_delay
                    self.orig_[id_] = j
                    id_ += 1
            id_ = 0        
            for iii in range(0,24):
                for kkk in range(1,60/self.dt + 1):
                    pushback_count = 0
                    takeoff_count = 0
                    ground_count = 0
                    t1 = 100*iii + (kkk-1)*self.dt
                    delay_count = 0
                    if kkk == 12:
                        t2 = 100*(iii+1) 
                    else:
                        t2 = 100*iii +kkk*self.dt
                    for j in range(len(self.orig_)):
                        fl = self.orig_[j]
                        if fl.a_departure_time_gate >= t1:
                            if fl.a_departure_time_gate < t2:
                                pushback_count +=1
                        if fl.a_runway_departure >= t1:
                            if fl.a_runway_departure < t2:
                                takeoff_count +=1
                                delay_count += fl.delay
                        if fl.a_departure_time_gate <= t1:
                            if fl.a_runway_departure >= t1:
                                ground_count += 1
                    if takeoff_count == 0:
                        self.aD[_counter] = self.aD[_counter-1]
                    else:
                        self.aD[_counter] = delay_count/takeoff_count
                    self.aN[_counter] = ground_count
                    self.aT[_counter] = takeoff_count
                    self.P[_counter] = pushback_count
                    for y in range(pushback_count):
                        self.flights[id_] = (cflight(self.id_,pushbacktime=_counter,aNpushback=ground_count))
                        self.id_ += 1
                        id_ += 1                    
                    _counter += 1
       
    cdef _solver(self,_counter):
        self.tt = []
        self.R[:] = 0
        self.N[0] = self.P[0]
        cdef int departure_counter = 0
        cdef int total, count, delay_timeline
        cdef numpy.ndarray count_
        for i in range(1,self.n/self.run-5):
            ii = _counter + i
            self.Q[ii] = self.Q[ii-1] - self.T[ii-1] + self.R[ii-1] - self.R[ii] + self.P[ii-1]
            
            self.N[ii] = self.Q[ii] + self.R[ii]
            
            total = 0
            count = 0
            while total <= self.dt:
                total += binomial(self.binomial_n,self.binomial_p)
                count += 1
            
            self.T[ii] = min(count,self.Q[ii])
            delay_timeline = 0
            if departure_counter + self.T[ii] < len(self.flights):
                for f in range(departure_counter,departure_counter+self.T[ii]):

                    if self.flights[f].pushbacktime <= i:
                        self.flights[f].departuretime = i
                    else:
                        self.flights[f].departuretime = self.flights[f].pushbacktime

                    self.flights[f].Ndeparture = self.N[ii]
                    delay_timeline += self.flights[f].departuretime - self.flights[f].pushbacktime

            if self.T[ii] == 0:
                self.D[ii] = self.D[ii-1]
            else:
                self.D[ii] = delay_timeline*self.dt/self.T[ii]

            departure_counter += self.T[ii]
            
            count_ = zeros(5)
            for j in range(self.P[ii]):
                tmp_gauss = gauss(self.tau_unimped_mean,self.tau_unimped_std) + self.alpha*self.R[ii]
                self.tt.append(tmp_gauss)
                if tmp_gauss <= self.dt:
                    count_[4] += 1
                elif tmp_gauss > self.dt and tmp_gauss <= self.dt*2:
                    count_[3] += 1
                elif tmp_gauss > self.dt*2 and tmp_gauss <= self.dt*3:
                    count_[2] += 1
                elif tmp_gauss > self.dt*3 and tmp_gauss <= self.dt*4:
                    count_[1] += 1
                else:
                    count_[0] += 1
            
            for j in range(5):
                for k in range(5-j):
                    self.R[ii+j+1] += count_[k]


    def plotgroundtraffic(self):
        (bins, n) = histOutline(self.N)
        plot(bins, n, 'r-',linewidth=2)
        #(bins, n) = histOutline(self.N,30)
        #plot(bins, n, 'g-',linewidth=2)
        hist(self.aN)
        title(str(self.alpha))
        xlim(0,25)
        savefig('/home/anand/pyatm/png/alpha_'+str(self.alpha)+'.png')
        clf()
    def init(self):
        t = time.time()
        self._initbase()
        n = (24*60/self.dt)
        for i in range(1,self.run):
            a = i*n
            b = (i+1)*n
            self.P[a:b] = self.P[:n]
            self.aN[a:b] = self.aN[:n]
            self.aT[a:b] = self.aT[:n]
            self.aD[a:b] = self.aD[:n]
            
        print "Initialised time taken ", time.time() - t
    def go(self):
        t = time.time()
        print "Running now .."
        for i in range(self.run):
            self._solver(i*24*len(self.day_list)*60/self.dt)
        print time.time() - t
        savez('data_run_aug_09_alpha'+str(self.alpha)+'_run_'+str(self.run)+'.npz',flights=self.flights,solver=self)
        print "Done!!"

    def start(self):
        self.init()
        self.go()
        self._plot()
       
    def _save_(self):
        takeoff_list = self.T
        atakeoff_list = self.aT
        ground_list = self.N

        cor = []
        avg = []
        aavg = []
        dt = range(-10,25)
        for j in range(22,len(takeoff_list)-22):
            tmp = []
            tmpj = []
            for k in range(-10,10):
                tmp.append(takeoff_list[j+k])
                tmpj.append(atakeoff_list[j+k])
            avg.append(mean(tmp))
            aavg.append(mean(tmpj))

        np.savez('_data.npz',_tp=ground_list[22:len(takeoff_list)-22],avg=avg,aavg=aavg,_tpa=self.aN[22:len(takeoff_list)-22])
            

   
    def _read_data(self):
        os.chdir('/home/anand/flightdata/npz')
        self.file_ = np.load('data_run_aug_09_alpha'+str(self.alpha)+'.npz')
        
        
    

    def _plot(self):
        figure()
        title(self.day_list[0])
        self.at = self.aD.reshape(self.run,24*len(self.day_list)*60/self.dt)
        self.t = self.D.reshape(self.run,24*len(self.day_list)*60/self.dt)
        n = 24*60/self.dt/96
        self.mean_ = []
        self.std_ = []

        for i in range(24*len(self.day_list)*60/self.dt):
            self.mean_.append(mean(self.t[:,i]))
            self.std_.append(std(self.t[:,i]))
       
        
        self.at = self.at[0,:].reshape(96,n)
        self.t = array(self.mean_).reshape(96,n)
        self.sstd_ = array(self.std_).reshape(96,n)
        
        at_ = []
        t_ = []
        std_ = []
        n_ = float(n)
        for i in range(96):
            at_.append(sum(self.at[i,:])/n_)
            t_.append(sum(self.t[i,:])/n_)
            std_.append(sum(self.sstd_[i,:])/n_)
        subplot(311)
        errorbar(range(96), t_, yerr=std_,linewidth=2)      
        plot(range(96),at_,linewidth=2)
        xlabel('Time (15min)')
        ylabel('Avg Delay(min)')
        xlim(40,80)
        ylim(0,35)

        subplot(312)
        self.at = self.aT.reshape(self.run,24*len(self.day_list)*60/self.dt)
        self.t = self.T.reshape(self.run,24*len(self.day_list)*60/self.dt)

        self.mean_ = []
        self.std_ = []

        for i in range(24*len(self.day_list)*60/self.dt):
            self.mean_.append(mean(self.t[:,i]))
            self.std_.append(std(self.t[:,i]))
       
        
        self.at = self.at[0,:].reshape(96,n)
        self.t = array(self.mean_).reshape(96,n)
        self.sstd_ = array(self.std_).reshape(96,n)
        
        at_ = []
        t_ = []
        std_ = []
        
        for i in range(96):
            at_.append(sum(self.at[i,:]))
            t_.append(sum(self.t[i,:]))
            std_.append(sum(self.sstd_[i,:])/n_)
        errorbar(range(96), t_, yerr=std_,linewidth=2)      
        plot(range(96),at_,linewidth=2)
        xlabel('Time (15min)')
        ylabel('Takeoff Rate (per 15 min)')
        xlim(40,80)
        ylim(0,15)

        subplot(313)
        self.at = self.aN.reshape(self.run,self.n/self.run)
        self.t = self.N.reshape(self.run,self.n/self.run)

        self.mean_ = []
        self.std_ = []

        for i in range(24*len(self.day_list)*60/self.dt):
            self.mean_.append(mean(self.t[:,i]))
            self.std_.append(std(self.t[:,i]))
       
        
        self.at = self.at[0,:].reshape(96,n)
        self.t = array(self.mean_).reshape(96,n)
        self.sstd_ = array(self.std_).reshape(96,n)
        
        at_ = []
        t_ = []
        std_ = []
        
        for i in range(96):
            at_.append(sum(self.at[i,:])/n_)
            t_.append(sum(self.t[i,:])/n_)
            std_.append(sum(self.sstd_[i,:])/n_)
        errorbar(range(96), t_, yerr=std_,linewidth=2)      
        plot(range(96),at_,linewidth=2)
        xlabel('Time (15min)')
        ylabel('N(t)')
        xlim(40,80)
        ylim(0,16)
        savefig('/home/anand/pyatm/data/'+self.day_list[0]+'.png')
if __name__ == "__main__":
    a = Solver()
    a.start()
